//
// Created by suzhen on 17-3-9.
//

#ifndef ORIONV2_COROUTINE_H
#define ORIONV2_COROUTINE_H

#include "Traits.hpp"
#include "CoroutineCore.h"


ORION_V2_NAMESPACE_BEGIN

        namespace Coroutine {

            class CoroutineMgr {
            public:
                ~CoroutineMgr() {
                    if (gen_) {
                        BasicEventGenerator::CloseGenerator(gen_);
                        gen_->release();
                    }
                }

                CoroutineMgr() {
                    std::size_t coreNums = Utils::GetCoreNum();
                    for (std::size_t i = 0; i < coreNums; ++i) {
                        genVec_.emplace_back(CoroutineCore(i, YES));
                    }
                    gen_ = BasicEventGenerator::CreateGenerator();
                    gen_->retain();
                    gen_->startAsync([]() {
                        boost::fibers::use_scheduling_algorithm<boost::fibers::algo::shared_work>();
                    });
                }

                template<typename F,
                        typename = typename std::enable_if_t<TypeTraits::IsCallable<F>::value>
                >
                void submitTask(F &&f) {
                    gen_->submitTask([this, f = std::move(f)](BasicEventGenerator &) mutable {
                        boost::fibers::fiber{std::move(f)}.detach();
                        std::for_each(genVec_.begin(), genVec_.end(), [](CoroutineCore &core) {
                            core.notify();
                        });
                    });
                }

            private:
                BasicEventGenerator *gen_;
                std::vector<CoroutineCore> genVec_;

            public:
                CoroutineMgr(const CoroutineMgr &) = delete;

                CoroutineMgr &operator=(const CoroutineMgr &) = delete;

                CoroutineMgr(CoroutineMgr &&o) : gen_(o.gen_),
                                                 genVec_(std::move(o.genVec_)) {
                    o.gen_ = nullptr;
                }

                CoroutineMgr &operator=(CoroutineMgr &&o) {
                    if (this == &o) {
                        return *this;
                    }
                    gen_ = o.gen_;
                    genVec_ = std::move(o.genVec_);
                    o.gen_ = nullptr;
                    return *this;
                }
            };

            extern CoroutineMgr &InitCoroutine();

            extern CoroutineMgr &GetCoroutineMgr();

            template<typename F>
            void go(F &&f) {
                GetCoroutineMgr().submitTask(std::forward<F>(f));
            }

        }

ORION_V2_NAMESPACE_END

#endif //ALASKA_COROUTINE_H
